package cn.longwingstech.intelligence.agent.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import cn.longwingstech.intelligence.agent.aop.RateLimiter;
import cn.longwingstech.intelligence.agent.app.DoctorApp;
import cn.longwingstech.intelligence.agent.app.config.AiConfigProperties;
import cn.longwingstech.intelligence.agent.app.manus.LongManus;
import cn.longwingstech.intelligence.agent.common.BaseResponse;
import cn.longwingstech.intelligence.agent.common.ResultUtils;
import cn.longwingstech.intelligence.agent.langchain4j.SuperAgent;
import cn.longwingstech.intelligence.agent.model.entity.App;
import cn.longwingstech.intelligence.agent.service.AppService;
import cn.longwingstech.intelligence.agent.service.ChatHistoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@RestController
@RequestMapping("/ai")
@SaCheckLogin
@Tag(name = "智能助手控制层")
public class AiController {
    @Resource
    private DoctorApp doctorApp;
    @Resource
    private ChatHistoryService chatHistoryService;
    @Resource
    private AppService appService;
    @Resource
    private SuperAgent superAgent;
    @Resource
    @Qualifier("allTools")
    private ToolCallback[] allTools;
    @Resource
    private AiConfigProperties aiConfigProperties;

    /**
     * 医生智能体
     *
     * @param message
     * @param chatId
     * @return
     */
    @GetMapping(value = "/doctor_app/chat/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE + ";charset=UTF-8")
    @SaIgnore
    @Transactional
    @Operation(summary = "灵溪医疗助手智能体")
    @RateLimiter(key = "doctorAppDoSSE", time = 60, count = 5)
    public Flux<String> doctorAppDoSSE(@RequestParam("message") String message, @RequestParam("chatId") String chatId) {
        StpUtil.checkLogin();
        Assert.hasText(message, "消息不能为空");
        Assert.hasText(chatId, "chatId房间号不能为空");
        // 保存用户请求消息
        App app = appService.getOne(new LambdaQueryWrapper<App>().eq(App::getChatId, chatId));
        if (app == null) {
            // 生成标题
            String title = doctorApp.titleGen(message);
            // 保存app
            app = new App();
            app.setChatId(chatId);
            app.setTitle(title);
            app.setUserId(StpUtil.getLoginIdAsLong());
            boolean save = appService.save(app);
            Assert.isTrue(save, "保存失败");
        }
        chatHistoryService.saveUserMessage(message, app.getId());

        // 创建一个可以取消的信号 用于用户手动中断请求,后端可以中断和ai的链接
        AtomicBoolean isCancelled = new AtomicBoolean(false);
        Flux<String> flux = doctorApp.chat(chatId, message).takeUntil(other -> isCancelled.get());
        StringBuffer tempStr = new StringBuffer();
        Long appid = app.getId();
        return flux
                .doOnNext(tempStr::append)
                .doOnComplete(() -> {
                    chatHistoryService.saveAssistantMessage(tempStr.toString(), appid);
                }).doOnError(e -> {
                    isCancelled.set(true);
                    chatHistoryService.saveAssistantMessage(tempStr.toString() + "请求错误,错误信息：" + e.getMessage(), appid);
                })
                .doOnCancel(() -> {
                    chatHistoryService.saveAssistantMessage(tempStr.toString(), appid);
                    isCancelled.set(true);
                });

    }

    /**
     * 生成聊天标题
     *
     * @param message
     * @param chatId
     * @return
     */

    @GetMapping("/genTitle")
    @Operation(summary = "生成标题")
    @RateLimiter(key = "genTitle", time = 60, count = 10)
    public BaseResponse<String> genTitle(@RequestParam String message, @RequestParam String chatId) {
        Assert.hasText(message, "消息不能为空");
        Assert.hasText(chatId, "房间号不能为空");
        String title = doctorApp.titleGen(message);
        App app = new App();
        app.setChatId(chatId);
        app.setTitle(title);
        app.setUserId(StpUtil.getLoginIdAsLong());
        boolean save = appService.save(app);
        Assert.isTrue(save, "保存失败");
        return ResultUtils.success(title);
    }


    @GetMapping(value = "/super/agent/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @SaIgnore
    @Operation(summary = "超级智能体")
    @RateLimiter(key = "superAgent", time = 60, count = 5)
    public Flux<String> superAgent(@RequestParam String message, @RequestParam("chatId") String chatId) {
        StpUtil.checkLogin();
        return superAgent.chat(message, chatId);
    }
//    @GetMapping("/longManus")
    @SaIgnore
    public BaseResponse<String> longManus(@RequestParam String message) {
        OpenAiApi openAiApi = OpenAiApi.builder()
                .baseUrl(aiConfigProperties.getBaseUrl())
                .apiKey(aiConfigProperties.getApiKey())
                .build();

        OpenAiChatOptions options = OpenAiChatOptions.builder()
                .model(aiConfigProperties.getModel())
                .build();

        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .openAiApi(openAiApi)
                .defaultOptions(options)
                .build();

        LongManus longManus = new LongManus(allTools, openAiChatModel);

        return ResultUtils.success(longManus.run(message));
    }
}
